﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Globalization;

namespace SISREBBVA.BE
{
    public class BBVA
    {

        #region "Propiedades"

        private static string URL_CATEGORIAS = "https://api.bbva.com/apidatos/info/merchants_categories.";
        private static string URL_CUSTOMER_ZIPCODES = "https://api.bbva.com/apidatos/zones/customer_zipcodes.";
        private static string URL_CARDS_CUBE = "https://api.bbva.com/apidatos/zones/cards_cube.";
        private static string URL_CONSUMPTION_PATTER = "https://api.bbva.com/apidatos/zones/consumption_pattern.";
        private static string USER_AGENT = "Mozilla/5.0";


        public string cadena_CustomerZipCodesByCategoriasByUsuario { get; set; }
        public string cadena_ConsumoByZipCodeByCategoriasByUsuario { get; set; }
        
        List<Categoria> listacategorias;
        Usuario usuario;
        Dictionary<String, List<CustomerZipcode>> listaZipcodes;

        

        public Usuario CurrentUsuario
        {
            get
            {
                usuario = usuario ?? new Usuario();
                return usuario;
            }
            set
            {
                usuario = value;
            }
        }

        public List<Categoria> ListaCatgorias
        {
            get
            {
                listacategorias = listacategorias ?? new List<Categoria>();
                return listacategorias;
            }
            set
            {
                listacategorias = value;
            }
        }

        public Dictionary<String, List<CustomerZipcode>> ListaCustomerZipCodes
        {
            get
            {
                listaZipcodes = listaZipcodes ?? new Dictionary<String, List<CustomerZipcode>>();
                return listaZipcodes;
            }
            set
            {
                listaZipcodes = value;
            }
        }
        
        #endregion

        #region "Singleton"

        private static BBVA _instance = null;

        public static BBVA GetInstance()
        {
            if (_instance == null)
            {
                _instance = new BBVA();
            }
            return _instance;
        }

        #endregion

        #region "sendGetCategories"
        public void sendGetCategories(String tipo_response)
        {

            HttpWebRequest connection = (HttpWebRequest)HttpWebRequest.Create(URL_CATEGORIAS + tipo_response);
            connection.Method = "GET";
            connection.Headers.Add("Authorization", Enum.CODE_APP.ToString());
            connection.UserAgent = Enum.USER_AGENT.ToString();
            HttpWebResponse response = (HttpWebResponse)connection.GetResponse();


            StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

            //lectura
            String inputLine;

            StringBuilder result = new StringBuilder();

            while ((inputLine = sr.ReadLine()) != null)
            {
                result.Append(inputLine);
            }
            sr.Close();


            List<Categoria> listaCategorias = desseralizarDataCategorias(result.ToString());
             GetInstance().ListaCatgorias = listaCategorias;

            
        }


        private List<Categoria> desseralizarDataCategorias(String xml)
        {

            List<Categoria> listaCategorias = new List<Categoria>();           
            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);


            XmlNodeList nodeLstCategorias = doc.GetElementsByTagName("category");
            for (int i = 0; i < nodeLstCategorias.Count; i++)
            {
                Categoria categoria = new Categoria();
                XmlNode nodoCat = nodeLstCategorias[i];

                String code = nodoCat.ChildNodes[0].InnerText;
                String des = nodoCat.ChildNodes[1].InnerText;

                categoria.code = code;
                categoria.descripcion = des;

                listaCategorias.Add(categoria);
            }


            return listaCategorias;
        }

        #endregion

        #region "sendGetCustomerZipcodes" 
        public  Dictionary<String, List<CustomerZipcode>> sendGetCustomerZipcodes(String tipo_response, String date_min, String date_max, String groupBy, String cod_Category, 
                                   String zipCode, float latitud, float longitud, int zoom, int depth, String by)
        {
        
            // Restricciones de parámetros:
            // 1.- response     -> Possible values : json or xml
            // 2.- date_min     -> Accepted formats: yyyymmdd, yyyymm
            // 3.- dae_max      -> Accepted formats: yyyymmdd, yyyymm
            // 4.- groupBy      -> Possible values : week or month
            // 5.- cod_Category -> Accepted codes  : Los "codigos" de las categorías ya cargadas.
            // 6.- zipCode      -> Accepted format : zipcode (5 digits). (Necesita Zoom, Latitud y Longitud, obligatorios)
            // 7.- latitud      -> Accepted format : XX.XXXXX (No debe setearse el ZipCode) 
            // 8.- longitud     -> Accepted format : XX.XXXXX (No debe setearse el ZipCode) 
            // 9.- zoom         -> Accepted values : 2. (No debe setearse el ZipCode)
            // 10.- depth       -> Possible values : 0,1,2(0: celda única, 1: barrio con 9 vecinos, 2: barrio con 25 vecinos)
            // 11.- by          -> Possible values : cards, payments or incomes(Indican cómo serán ordenados los zipcodes)
        
        
            String urlParametrizada = URL_CUSTOMER_ZIPCODES+tipo_response+"?"+"date_min="+date_min+"&"+"date_max="+date_max+"&"
                                               +"group_by="+groupBy;
        
            if(cod_Category != string.Empty)
                urlParametrizada=urlParametrizada+"&"+"category="+cod_Category;
            if(zipCode != string.Empty){
                urlParametrizada=urlParametrizada+"&"+"zipcode="+zipCode;
            }
            if(latitud!=0.0000 && longitud != 0.000) {
                urlParametrizada = urlParametrizada+"&"+"latitude="+latitud+"&"+"longitude="+longitud;
            }
            if(zoom>0){
                urlParametrizada = urlParametrizada+"&"+"zoom="+zoom;
            }
            if(depth>0){
                urlParametrizada = urlParametrizada+"&"+"depth="+depth;
            }
        
            urlParametrizada = urlParametrizada+"&"+"by="+by;

            HttpWebRequest connection = (HttpWebRequest)HttpWebRequest.Create(urlParametrizada);
            connection.Method = "GET";
            connection.Headers.Add("Authorization", Enum.CODE_APP.ToString());
            connection.UserAgent = Enum.USER_AGENT.ToString();
            HttpWebResponse response = (HttpWebResponse)connection.GetResponse();


            StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

            //lectura
            String inputLine;

            StringBuilder result = new StringBuilder();

            while ((inputLine = sr.ReadLine()) != null)
            {
                result.Append(inputLine);
            }
            sr.Close();
        
            Dictionary<String, List<CustomerZipcode>> mapa = desserializarDataCustomeZipCodes(result.ToString());
            GetInstance().ListaCustomerZipCodes = mapa ;
                
            return mapa;
        }


    
        private Dictionary<String, List<CustomerZipcode>> desserializarDataCustomeZipCodes(String xml) 
        {
            List<CustomerZipcode> listaZipcodes = new List<CustomerZipcode>();

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);


            Dictionary<String, List<CustomerZipcode>> mapa = new Dictionary<String, List<CustomerZipcode>>();

            XmlNodeList nlMeses = doc.GetElementsByTagName("date");
            XmlNodeList nlZipcodes = doc.GetElementsByTagName("zipcodes");
                   
       
           for(int i=0; i<nlMeses.Count; i++){
                 CustomerZipcode zipcode = new CustomerZipcode();
                 XmlNode nodoMes = nlMeses[i];
                 XmlNode nodoZipcode = nlZipcodes[i];
             
                 String mes = nodoMes.InnerText;
                 XmlNodeList listaItems = nodoZipcode.ChildNodes;
             
                 List<CustomerZipcode> listaZipcodesInter = new List<CustomerZipcode>();
                 for(int j=1; j<=listaItems.Count; j++){
                 
                     XmlNode item = listaItems[j];
                 
                     if(item != null){
                        if(item.NodeType == XmlNodeType.Element){
                        
                         zipcode = new CustomerZipcode();
                         zipcode.zipcode = item.ChildNodes[0].InnerText;
                         zipcode.numCards = Int32.Parse(item.ChildNodes[1].InnerText);
                         zipcode.numPayments = Int32.Parse(item.ChildNodes[2].InnerText);
                         zipcode.incomes =  float.Parse(item.ChildNodes[3].InnerText);
                     
                         listaZipcodesInter.Add(zipcode);
                        }
                     }
                 }        
             
                 mapa.Add(mes, listaZipcodesInter);
             }
         
         
             return mapa;
        }

        #endregion

        #region "sendGetCardsCube"
    
        public  Dictionary<String, List<CardCube>> sendGetCardsCube(String tipo_response, String date_min, String date_max, String groupBy, String cod_Category, 
                                   String zipCode, float latitud, float longitud, int zoom, int depth) 
        {
        
            // Restricciones de parámetros:
            // 1.- response     -> Possible values : json or xml
            // 2.- date_min     -> Accepted formats: yyyymmdd, yyyymm
            // 3.- dae_max      -> Accepted formats: yyyymmdd, yyyymm
            // 4.- groupBy      -> Possible values : week or month
            // 5.- cod_Category -> Accepted codes  : Los "codigos" de las categorías ya cargadas.
            // 6.- zipCode      -> Accepted format : zipcode (5 digits). (Necesita Zoom, Latitud y Longitud, obligatorios)
            // 7.- latitud      -> Accepted format : XX.XXXXX (No debe setearse el ZipCode) 
            // 8.- longitud     -> Accepted format : XX.XXXXX (No debe setearse el ZipCode) 
            // 9.- zoom         -> Accepted values : 2. (No debe setearse el ZipCode)
            // 10.- depth       -> Possible values : 0,1,2(0: celda única, 1: barrio con 9 vecinos, 2: barrio con 25 vecinos)
                
        
            String urlParametrizada = URL_CARDS_CUBE+tipo_response+"?"+"date_min="+date_min+"&"+"date_max="+date_max+"&"
                                               +"group_by="+groupBy;
        
            if(cod_Category  != string.Empty)
                urlParametrizada=urlParametrizada+"&"+"category="+cod_Category;
            if(zipCode != string.Empty){
                urlParametrizada=urlParametrizada+"&"+"zipcode="+zipCode;
            }
            if(latitud!=0.0000 && longitud != 0.000) {
                urlParametrizada = urlParametrizada+"&"+"latitude="+latitud+"&"+"longitude="+longitud;
            }
            if(zoom>0){
                urlParametrizada = urlParametrizada+"&"+"zoom="+zoom;
            }
            if(depth>0){
                urlParametrizada = urlParametrizada+"&"+"depth="+depth;
            }
        
            HttpWebRequest connection = (HttpWebRequest)HttpWebRequest.Create(urlParametrizada);
            connection.Method = "GET";
            connection.Headers.Add("Authorization", Enum.CODE_APP.ToString());
            connection.UserAgent = Enum.USER_AGENT.ToString();
            HttpWebResponse response = (HttpWebResponse)connection.GetResponse();


            StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

            //lectura
            String inputLine;

            StringBuilder result = new StringBuilder();

            while ((inputLine = sr.ReadLine()) != null)
            {
                result.Append(inputLine);
            }
            sr.Close();



            Dictionary<String, List<CardCube>> mapa = desserializarDataCardsCube(result.ToString());
                
            return mapa;
           
        
        }


        private Dictionary<String, List<CardCube>> desserializarDataCardsCube(String xml) 
        {

            Dictionary<String, List<CardCube>> mapa = new Dictionary<String, List<CardCube>>();
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            XmlNodeList nlMeses = doc.GetElementsByTagName("date");
            XmlNodeList nlCubes = doc.GetElementsByTagName("cube");
                   
       
           for(int i=0; i<nlMeses.Count; i++){
                 CardCube cardcube = null;
                 XmlNode nodoMes = nlMeses[i];
                 XmlNode nodoCell = nlCubes[i];
             
                 String mes = nodoMes.InnerText;
                 XmlNodeList listaItems = nodoCell.ChildNodes;
             
                 List<CardCube> listaCardCubesInter = new List<CardCube>();
                 for(int j=1; j<=listaItems.Count; j++){

                     XmlNode item = listaItems[j];
                 
                     if(item != null){
                        if(item.NodeType == XmlNodeType.Element){
                        
                         cardcube = new CardCube();
                         String[] hash = item.ChildNodes[0].InnerText.ToString().Split('#');
                         if(hash.Length == 2){
                            cardcube.genero = hash[0] ;
                            cardcube.grupoEdad = hash[1] ;
                         }
                         cardcube.nroCards = Int32.Parse(item.ChildNodes[1].InnerText) ;
                         cardcube.nroPayments = Int32.Parse(item.ChildNodes[2].InnerText);
                         cardcube.promedioGasto = float.Parse(item.ChildNodes[3].InnerText);
                     
                         listaCardCubesInter.Add(cardcube);
                        }
                     }
                 }        
             
                 mapa.Add(mes, listaCardCubesInter);
             }
         
         
             return mapa;    
        }
        #endregion

        #region "getZipCodesCercanosByUsuario"
    
        public String getZipCodesCercanosByUsuario(Usuario usuario) 
        {
        
            String cadena="";
            foreach(Categoria categoria in usuario.listaCategorias){
                Dictionary<String, List<CustomerZipcode>> mapaZipCodes = sendGetCustomerZipcodes("xml", "201211", "201303", "month", categoria.code, usuario.codigoPostal, 0, 0, 2, 1, "payments");
            
                List<CustomerZipcode> listaFiltrada = new List<CustomerZipcode>();

                foreach (KeyValuePair<string,List<CustomerZipcode>> elemento in mapaZipCodes) {
                    List<CustomerZipcode> listaZipCodes = elemento.Value;
                    foreach(CustomerZipcode item in listaZipCodes){
                        listaFiltrada = esMejor(item.zipcode, item.numPayments, item.numCards, item.incomes, listaFiltrada);                
                   }
                }

                foreach(CustomerZipcode item in listaFiltrada){
                    cadena = cadena + item.zipcode + "#";
                }

                cadena_CustomerZipCodesByCategoriasByUsuario = cadena;
            }
            return cadena;
        }
    
        private List<CustomerZipcode> esMejor(String zipcdode, int nro_payments, int nro_cards, float incomes, List<CustomerZipcode> listaActual)
        {
            bool ok = false;
            for(int i=0; i<listaActual.Count; i++){
                if(listaActual[i].zipcode == zipcdode && nro_payments>listaActual[i].numPayments){
                    // si existe uno con mejor nro_payments actualizo los datos de ese item de la lista filtrada
                    // para siempre tener el mejor
                    listaActual[i].numCards  = nro_cards;
                    listaActual[i].incomes = incomes;
                    listaActual[i].numPayments = nro_payments;
                    ok = true;
                }
            }
        
            if(!ok){// si no encontro ningun es xq es uno nuevo
                CustomerZipcode zipcode = new CustomerZipcode();
                zipcode.zipcode = zipcdode;
                zipcode.incomes = incomes;
                zipcode.numCards = nro_cards;
                zipcode.numPayments = nro_payments;
                listaActual.Add(zipcode);
            }
            return listaActual;
        }
    
        #endregion    
    
        #region "getDatosConsumoByZicodeUsuario"
    
        public String getDatosConsumoByZicodeUsuario(Usuario usuario) {
        
            String cadena="";
            String sexo = usuario.sexo;
            String grupo_edad = obtenerGrupoEdad(usuario.fechanacimiento);
        
            foreach(Categoria categoria in usuario.listaCategorias){
                Dictionary<String, List<CardCube>> hmCardCubes =  GetInstance().sendGetCardsCube("xml", "201211", "201303", "month", categoria.code, usuario.codigoPostal, 0, 0, 2, 1);
            
            
                CardCube mejor = new CardCube();
                foreach (KeyValuePair<string, List<CardCube>> elemento in hmCardCubes) {
                    List<CardCube> listaCardCubes = elemento.Value;
                    foreach(CardCube item in listaCardCubes){
                        if(item.genero == sexo && item.grupoEdad == grupo_edad && item.promedioGasto >mejor.promedioGasto){
                            mejor = item;
                        }
                   }
                }


                cadena = cadena + categoria.descripcion + "#" + mejor.promedioGasto + "@";
            

                cadena_ConsumoByZipCodeByCategoriasByUsuario = cadena;
            }
            return cadena;
        }
    
   
    
        private String  obtenerGrupoEdad(DateTime fecha){
            int edad = calcularEdad(fecha);
            if(edad != -1){
                if(edad<19){
                    return Enum.AGE_UNDER_19.ToString();
                }else if(edad>=19 && edad <=25){
                    return Enum.AGE_BET_19_25.ToString();
                }else if(edad>=19 && edad <=25){
                    return Enum.AGE_BET_19_25.ToString();
                }else if(edad>=26 && edad <=35){
                    return Enum.AGE_BET_26_35.ToString();
                }else if(edad>=36 && edad <=45){
                    return Enum.AGE_BET_36_45.ToString();
                }else if(edad>=46 && edad <=55){
                    return Enum.AGE_BET_46_55.ToString();
                }else if(edad>=56 && edad <=65){
                    return Enum.AGE_BET_56_65.ToString();
                }else if(edad>65){
                    return Enum.AGE_GREAT_65.ToString();
                }
            }
            return Enum.AGE_UNKNOWN;
        }
    
        private int calcularEdad(DateTime fecha) {
        
            try {
              
                int age = 0;
                int factor = 0;
                DateTime birth = fecha;
                DateTime today = DateTime.Now;
               
                if (today.Month <= birth.Month) {
                    if (today.Month == birth.Month)
                    {
                        if (today > birth) {
                            factor = -1; //Aun no celebra su cumpleaÃ±os
                        }
                    } else {
                        factor = -1; //Aun no celebra su cumpleaÃ±os
                    }
                }
                age = (today.Year - birth.Year) + factor;
                return age;
            } catch (Exception e) {
                return -1;
            }

        }
        #endregion

    }
}
